%{
#include "lex.yy.c"
#include "main.h"

extern int yylineno;
extern int yycolumn;


%}

%union {
	ParseNode type_node;
}

%token <type_node> INT_CORRECT FLOAT_CORRECT ID_CORRECT INT_ERROR FLOAT_ERROR ID_ERROR SPACE SEMI COMMA ASSIGNOP RELOP PLUS MINUS STAR DIV AND OR DOT NOT TYPE LP RP LB RB LC RC STRUCT RETURN IF ELSE WHILE

%type <type_node> INT FLOAT ID Program ExtDefList ExtDef ExtDecList Specifier StructSpecifier OptTag Tag VarDec FunDec VarList ParamDec CompSt StmtList Stmt DefList Def DecList Dec Exp Args

%right ASSIGNOP
%left OR
%left AND
%left RELOP
%left PLUS MINUS
%left STAR DIV
%right NOT
%left LP RP RB LB DOT
%nonassoc ELSE

%%
/*High-level Definitions */
Program : ExtDefList							{ ParseNode child[1] = {$1}; $$ = newNode(yylineno, 1, "Program", child); nodeList[nodeNum] = $$; nodeNum++; printTree(); }
    	;
ExtDefList : ExtDef ExtDefList					{ ParseNode child[2] = {$1, $2}; $$ = newNode(yylineno, 2, "ExtDefList", child); nodeList[nodeNum] = $$; nodeNum++; }
	|											{ $$ = newNode(-1, -1, "ExtDefList", NULL); }
	;
ExtDef : Specifier ExtDecList SEMI				{ ParseNode child[3] = {$1, $2, $3}; $$ = newNode(yylineno, 3, "ExtDef", child); nodeList[nodeNum] = $$; nodeNum++; }
	| Specifier SEMI							{ ParseNode child[2] = {$1, $2}; $$ = newNode(yylineno, 2, "ExtDef", child); nodeList[nodeNum] = $$; nodeNum++; }
	| Specifier FunDec CompSt					{ ParseNode child[3] = {$1, $2, $3}; $$ = newNode(yylineno, 3, "ExtDef", child); nodeList[nodeNum] = $$; nodeNum++; }
	| error SEMI								{  }
	;
ExtDecList : VarDec								{ ParseNode child[1] = {$1}; $$ = newNode(yylineno, 1, "ExtDecList", child); nodeList[nodeNum] = $$; nodeNum++; }
	| VarDec COMMA ExtDecList					{ ParseNode child[3] = {$1, $2, $3}; $$ = newNode(yylineno, 3, "ExtDecList", child); nodeList[nodeNum] = $$; nodeNum++; }
	;

/*Specifiers */
Specifier : TYPE								{ ParseNode child[1] = {$1}; $$ = newNode(yylineno, 1, "Specifier", child); nodeList[nodeNum] = $$; nodeNum++; }
	| StructSpecifier							{ ParseNode child[1] = {$1}; $$ = newNode(yylineno, 1, "Specifier", child); nodeList[nodeNum] = $$; nodeNum++; }
	;
StructSpecifier : STRUCT OptTag LC DefList RC	{ ParseNode child[5] = {$1, $2, $3, $4, $5}; $$ = newNode(yylineno, 5, "StructSpecifier", child); nodeList[nodeNum] = $$; nodeNum++; }
	| STRUCT Tag 								{ ParseNode child[2] = {$1, $2}; $$ = newNode(yylineno, 2, "StructSpecifier", child); nodeList[nodeNum] = $$; nodeNum++; }
	;
OptTag : ID 									{ ParseNode child[1] = {$1}; $$ = newNode(yylineno, 1, "OptTag", child); nodeList[nodeNum] = $$; nodeNum++; }
	|   										{ $$ = newNode(-1, -1, "OptTag", NULL); }
	;
Tag : ID 										{ ParseNode child[1] = {$1}; $$ = newNode(yylineno, 1, "Tag", child); nodeList[nodeNum] = $$; nodeNum++; }
	;

/*Declarators */
VarDec : ID 									{ ParseNode child[1] = {$1}; $$ = newNode(yylineno, 1, "VarDec", child); nodeList[nodeNum] = $$; nodeNum++; }
	| VarDec LB INT RB 							{ ParseNode child[4] = {$1, $2, $3, $4}; $$ = newNode(yylineno, 4, "VarDec", child); nodeList[nodeNum] = $$; nodeNum++; }
	;
FunDec : ID LP VarList RP 						{ ParseNode child[4] = {$1, $2, $3, $4}; $$ = newNode(yylineno, 4, "FunDec", child); nodeList[nodeNum] = $$; nodeNum++; }
	| ID LP RP 									{ ParseNode child[3] = {$1, $2, $3}; $$ = newNode(yylineno, 3, "FunDec", child); nodeList[nodeNum] = $$; nodeNum++; }
	;
VarList : ParamDec COMMA VarList 				{ ParseNode child[3] = {$1, $2, $3}; $$ = newNode(yylineno, 3, "VarList", child); nodeList[nodeNum] = $$; nodeNum++; }
	| ParamDec 									{ ParseNode child[1] = {$1}; $$ = newNode(yylineno, 1, "VarList", child); nodeList[nodeNum] = $$; nodeNum++; }
	;
ParamDec : Specifier VarDec 					{ ParseNode child[2] = {$1, $2}; $$ = newNode(yylineno, 2, "ParamDec", child); nodeList[nodeNum] = $$; nodeNum++; }
	;

/*Statements */
CompSt : LC DefList StmtList RC 				{ ParseNode child[4] = {$1, $2, $3, $4}; $$ = newNode(yylineno, 4, "CompSt", child); nodeList[nodeNum] = $$; nodeNum++; }
	;
StmtList : Stmt StmtList 						{ ParseNode child[2] = {$1, $2}; $$ = newNode(yylineno, 2, "StmtList", child); nodeList[nodeNum] = $$; nodeNum++; }
	|											{ $$ = newNode(-1, -1, "StmtList", NULL); }
	; 
Stmt : Exp SEMI 								{ ParseNode child[2] = {$1, $2}; $$ = newNode(yylineno, 2, "Stmt", child); nodeList[nodeNum] = $$; nodeNum++; }
	| CompSt 									{ ParseNode child[1] = {$1}; $$ = newNode(yylineno, 1, "Stmt", child); nodeList[nodeNum] = $$; nodeNum++; }
	| RETURN Exp SEMI 							{ ParseNode child[3] = {$1, $2, $3}; $$ = newNode(yylineno, 3, "Stmt", child); nodeList[nodeNum] = $$; nodeNum++; }
	| IF LP Exp RP Stmt 						{ ParseNode child[5] = {$1, $2, $3, $4, $5}; $$ = newNode(yylineno, 5, "Stmt", child); nodeList[nodeNum] = $$; nodeNum++; }
	| IF LP Exp RP Stmt ELSE Stmt 				{ ParseNode child[7] = {$1, $2, $3, $4, $5, $6, $7}; $$ = newNode(yylineno, 7, "Stmt", child); nodeList[nodeNum] = $$; nodeNum++; }
	| WHILE LP Exp RP Stmt 						{ ParseNode child[5] = {$1, $2, $3, $4, $5}; $$ = newNode(yylineno, 5, "Stmt", child); nodeList[nodeNum] = $$; nodeNum++; }
	| error RP									{  }
	;

/*Local Definitions */
DefList : Def DefList 							{ ParseNode child[2] = {$1, $2}; $$ = newNode(yylineno, 2, "DefList", child); nodeList[nodeNum] = $$; nodeNum++; }
	|											{ $$ = newNode(-1, -1, "DefList", NULL); }
	; 
Def : Specifier DecList SEMI 					{ ParseNode child[3] = {$1, $2, $3}; $$ = newNode(yylineno, 3, "Def", child); nodeList[nodeNum] = $$; nodeNum++; }
	;
DecList : Dec 									{ ParseNode child[1] = {$1}; $$ = newNode(yylineno, 1, "DecList", child); nodeList[nodeNum] = $$; nodeNum++; }
	| Dec COMMA DecList 						{ ParseNode child[3] = {$1, $2, $3}; $$ = newNode(yylineno, 3, "DecList", child); nodeList[nodeNum] = $$; nodeNum++; }
	;
Dec : VarDec 									{ ParseNode child[1] = {$1}; $$ = newNode(yylineno, 1, "Dec", child); nodeList[nodeNum] = $$; nodeNum++; }
	| VarDec ASSIGNOP Exp 						{ ParseNode child[3] = {$1, $2, $3}; $$ = newNode(yylineno, 3, "Dec", child); nodeList[nodeNum] = $$; nodeNum++; }
	;

/*Expressions */
Exp : Exp ASSIGNOP Exp 							{ ParseNode child[3] = {$1, $2, $3}; $$ = newNode(yylineno, 3, "Exp", child); nodeList[nodeNum] = $$; nodeNum++; }
	| Exp AND Exp 								{ ParseNode child[3] = {$1, $2, $3}; $$ = newNode(yylineno, 3, "Exp", child); nodeList[nodeNum] = $$; nodeNum++; }
	| Exp OR Exp 								{ ParseNode child[3] = {$1, $2, $3}; $$ = newNode(yylineno, 3, "Exp", child); nodeList[nodeNum] = $$; nodeNum++; }
	| Exp RELOP Exp 							{ ParseNode child[3] = {$1, $2, $3}; $$ = newNode(yylineno, 3, "Exp", child); nodeList[nodeNum] = $$; nodeNum++; }
	| Exp PLUS Exp 								{ ParseNode child[3] = {$1, $2, $3}; $$ = newNode(yylineno, 3, "Exp", child); nodeList[nodeNum] = $$; nodeNum++; }
	| Exp MINUS Exp 							{ ParseNode child[3] = {$1, $2, $3}; $$ = newNode(yylineno, 3, "Exp", child); nodeList[nodeNum] = $$; nodeNum++; }
	| Exp STAR Exp 								{ ParseNode child[3] = {$1, $2, $3}; $$ = newNode(yylineno, 3, "Exp", child); nodeList[nodeNum] = $$; nodeNum++; }
	| Exp DIV Exp 								{ ParseNode child[3] = {$1, $2, $3}; $$ = newNode(yylineno, 3, "Exp", child); nodeList[nodeNum] = $$; nodeNum++; }
	| LP Exp RP 								{ ParseNode child[3] = {$1, $2, $3}; $$ = newNode(yylineno, 3, "Exp", child); nodeList[nodeNum] = $$; nodeNum++; }
	| MINUS Exp 								{ ParseNode child[2] = {$1, $2}; $$ = newNode(yylineno, 2, "Exp", child); nodeList[nodeNum] = $$; nodeNum++; }
	| NOT Exp 									{ ParseNode child[2] = {$1, $2}; $$ = newNode(yylineno, 2, "Exp", child); nodeList[nodeNum] = $$; nodeNum++; }
	| ID LP Args RP 							{ ParseNode child[4] = {$1, $2, $3, $4}; $$ = newNode(yylineno, 4, "Exp", child); nodeList[nodeNum] = $$; nodeNum++; }
	| ID LP RP 									{ ParseNode child[3] = {$1, $2, $3}; $$ = newNode(yylineno, 3, "Exp", child); nodeList[nodeNum] = $$; nodeNum++; }
	| Exp LB Exp RB 							{ ParseNode child[4] = {$1, $2, $3, $4}; $$ = newNode(yylineno, 4, "Exp", child); nodeList[nodeNum] = $$; nodeNum++; }
	| Exp DOT ID 								{ ParseNode child[3] = {$1, $2, $3}; $$ = newNode(yylineno, 3, "Exp", child); nodeList[nodeNum] = $$; nodeNum++; }
	| ID 										{ ParseNode child[1] = {$1}; $$ = newNode(yylineno, 1, "Exp", child); nodeList[nodeNum] = $$; nodeNum++; }
	| INT 										{ ParseNode child[1] = {$1}; $$ = newNode(yylineno, 1, "Exp", child); nodeList[nodeNum] = $$; nodeNum++; }
	| FLOAT 									{ ParseNode child[1] = {$1}; $$ = newNode(yylineno, 1, "Exp", child); nodeList[nodeNum] = $$; nodeNum++; }
	;
Args : Exp COMMA Args 							{ ParseNode child[3] = {$1, $2, $3}; $$ = newNode(yylineno, 3, "Args", child); nodeList[nodeNum] = $$; nodeNum++; }
	| Exp										{ ParseNode child[1] = {$1}; $$ = newNode(yylineno, 1, "Args", child); nodeList[nodeNum] = $$; nodeNum++; }
	;

/*Error*/
ID : ID_CORRECT									{  }
	| ID_ERROR									{ $$ = newNode(yylineno, -1, "Def", NULL); nodeList[nodeNum] = $$; nodeNum++; yyerror("Invalid Identifier"); }
	;
FLOAT : FLOAT_CORRECT							{  }
	| FLOAT_ERROR								{ $$ = newNode(yylineno, -1, "Def", NULL); nodeList[nodeNum] = $$; nodeNum++; yyerror("Invalid float"); }
	;
INT : INT_CORRECT								{  }
	| INT_ERROR									{ $$ = newNode(yylineno, -1, "Def", NULL); nodeList[nodeNum] = $$; nodeNum++; yyerror("Invalid integer"); }
	;
	
%% 




